vLLM se ha asentado como la referencia para servir modelos de lenguaje en GPU cuando lo que importa es el rendimiento agregado. Su algoritmo PagedAttention gestiona la caché KV como si fuera memoria paginada del sistema operativo, y esa decisión arquitectónica explica la mayor parte de la diferencia frente a implementaciones ingenuas. Este artículo recoge lo que he aprendido desplegándolo en producción y, sobre todo, cuándo tiene sentido y cuándo no.
Por qué PagedAttention cambia las reglas
Los servidores de inferencia tradicionales reservan memoria contigua por petición para guardar la caché de atención, lo que provoca fragmentación enorme cuando conviven peticiones de longitudes muy distintas en la misma GPU. PagedAttention rompe esa caché en bloques pequeños de tamaño fijo y los gestiona con una tabla de páginas, exactamente como hace un kernel con la RAM. El resultado es que puedes meter muchísimas más peticiones simultáneas en la misma tarjeta sin tocar los pesos.
Sobre esa base se apila el batching continuo: las peticiones entran y salen del lote en curso sin esperar a que termine el grupo anterior, lo que elimina los huecos de GPU ociosa que aparecen cuando unas respuestas son cortas y otras largas. En la práctica, la mejora de throughput frente a un servidor ingenuo puede ir de tres a veinticuatro veces según la mezcla de tráfico, y esa cifra no es marketing: es matemática de ocupación de memoria.
Además, vLLM expone una API compatible con la de OpenAI, soporta paralelismo de tensores para repartir un modelo entre varias GPU, ofrece cuantización en formatos AWQ, GPTQ, FP8 e INT8, y cubre la familia abierta relevante: Llama, Mistral, Qwen, DeepSeek, Phi y Gemma.
Instalación y primer arranque
El punto de entrada mínimo es un único comando que levanta un servidor HTTP con la API de OpenAI lista para consumir desde cualquier cliente existente:
pip install vllm
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Meta-Llama-3-8B-Instruct \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.95 \
--max-model-len 32768 \
--quantization awq
El endpoint queda en http://localhost:8000/v1 y cualquier SDK que hable con la API de OpenAI funciona cambiando únicamente la URL base y pasando una clave ficticia. Esto es, en mi experiencia, lo que más acelera la adopción: el código de aplicación no cambia.
Los parámetros que mueven la aguja son cuatro. El tamaño de paralelismo de tensores indica en cuántas GPU se reparte el modelo y conviene ajustarlo al número de tarjetas físicas por nodo. La utilización de memoria suele situarse entre el noventa y el noventa y cinco por ciento; subirla más deja al runtime sin margen para picos y provoca errores de OOM difíciles de reproducir. La longitud máxima de contexto condiciona cuánta caché KV reservas, así que ampliarla a treinta y dos mil tokens cuando tu tráfico real usa dos mil es regalar memoria. Y la cuantización, cuando el modelo la admite, es casi siempre una victoria neta.
Cuantización, paralelismo y rendimiento real
AWQ ofrece la mejor relación calidad-memoria en la mayoría de modelos Llama y Mistral y los pesos ya vienen pre-cuantizados en Hugging Face, por lo que el arranque es inmediato. GPTQ es equivalente en espíritu pero con otro formato. FP8 es interesante sólo si tienes H100, porque en A100 cae a caminos lentos. INT4 comprime muchísimo pero empieza a degradar el razonamiento en cadenas largas, y eso no siempre se nota en benchmarks cortos.
El paralelismo de tensores es imprescindible en modelos que no caben en una sola GPU: un Llama 3.1 de setenta mil millones en FP16 pide cuatro A100 de ochenta gigas, mientras que con AWQ cabe cómodamente en dos. El paralelismo de pipeline sólo compensa si ya has agotado el tensor parallelism dentro del nodo y necesitas cruzar a otro servidor; la latencia entre nodos penaliza mucho el primer token.
En rendimiento concreto, un Llama 3 de ocho mil millones sobre una A100 de ochenta gigas me da unos sesenta a ochenta tokens por segundo en peticiones aisladas, pero con cincuenta conexiones concurrentes el agregado se dispara a dos o tres mil tokens por segundo. Ese salto es precisamente el regalo de PagedAttention y el batching continuo, y es lo que hace que el mismo hardware sea órdenes de magnitud más barato por token en carga real que sirviendo de una en una.
Observabilidad y funciones avanzadas
vLLM expone métricas de Prometheus activando un puerto dedicado. Las cinco que miro siempre son peticiones en curso, peticiones en cola, ocupación de la caché KV de GPU, tiempo hasta el primer token y latencia extremo a extremo. El dashboard oficial de Grafana las cubre, y lo importante es vigilar la cola: si crece sostenida, no tienes un problema de modelo sino de capacidad, y toca escalar con réplicas o cambiar a una tarjeta más potente.
De las funciones avanzadas, tres merecen mención. Multi-LoRA permite servir varios adaptadores sobre el mismo modelo base y conmutar por petición, lo cual es oro cuando has hecho varios fine-tunes pequeños. El decodificado especulativo usa un modelo borrador que propone tokens y el principal los verifica, con aceleraciones realistas de dos a tres veces. Y la salida estructurada, vía integración con Outlines, garantiza JSON válido contra un esquema, lo que elimina toda una categoría de parsers frágiles en la aplicación.
vLLM frente a TGI y SGLang
Text Generation Inference de Hugging Face mantiene ingeniería sólida e integración impecable con el ecosistema HF, pero cambió a una licencia restrictiva que complica ciertos despliegues comerciales, y en throughput puro se queda algo por detrás. SGLang, más reciente, es fuerte en workloads con mucho prefijo compartido pero su comunidad es aún pequeña. LMDeploy brilla con la familia Intern y cuantización agresiva, aunque pierde fuelle fuera de ese nicho. vLLM ocupa el centro de gravedad: gana en throughput general, mantiene licencia Apache 2.0 y recibe mejoras casi cada semana.
Mi lectura
Si tu carga llega a varios millones de tokens diarios y controlas el hardware, self-hosting con vLLM compensa frente a pagar API por token antes de lo que la gente cree: el punto de equilibrio en tarifas de octubre de 2024 está en torno a los diez millones de tokens al día para un modelo mediano, y por debajo la API comercial sigue siendo más barata una vez contabilizas horas de ingeniería y electricidad. Por encima, el ahorro se vuelve agresivo y la soberanía sobre el modelo empieza a tener valor estratégico.
El principal error que veo es tratar vLLM como un binario que se enciende y se olvida. No lo es: requiere criterio para dimensionar contexto máximo, elegir cuantización, decidir réplicas horizontales y calibrar la utilización de memoria. También exige aceptar sus límites: sólo soporta bien NVIDIA (ROCm sigue experimental en la versión 0.6), el arranque carga varios gigabytes de pesos y puede tardar minutos, y algunos modelos menos populares tienen kernels menos optimizados.
Si vas a producción seria con LLM propios en 2024, yo empezaría con vLLM directamente y sólo miraría alternativas cuando un problema concreto me empujara fuera. Probar tres servidores antes de comprometerse consume semanas que casi nunca valen la información extra. Pon métricas desde el primer día, vigila cola y caché KV, y escala por réplicas cuando la cola crezca. Con eso cubres el noventa por ciento de los casos reales.